"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
const path = require("path");
const chalk = require("chalk");
const semver = require("semver");
const fse = require("fs-extra");
const os = require("os");
const render_package_1 = __importDefault(require("@x.render/render-package"));
const ora_1 = __importDefault(require("ora"));
const render_node_utils_1 = require("@x.render/render-node-utils");
const validateProjectName = require("validate-npm-package-name");
const render_command_1 = require("@x.render/render-command");
const constant_1 = require("../constant");
const inquirer = require("inquirer");
const prompt = inquirer.prompt;
class InitCommand extends render_command_1.RenderCommand {
    constructor(rest, options, cmd) {
        super(rest, options, cmd);
        this.force = false;
        this.appName = null;
        this.appVersion = null;
        this.pkgName = null;
        this.package = null;
        this.init();
    }
    init() {
        return __awaiter(this, void 0, void 0, function* () {
            const { force } = this.options;
            this.force = force;
            const { pkgName, appName, appVersion } = yield this.getAppInfo();
            this.appName = appName;
            this.pkgName = pkgName;
            this.appVersion = appVersion;
            this.package = new render_package_1.default({
                storePath: "packages",
                localPkgCachePath: constant_1.CLI_NAME,
                pkgName,
            });
            this.exec();
        });
    }
    getAppInfo() {
        return __awaiter(this, void 0, void 0, function* () {
            return {
                pkgName: yield this.getAppType(),
                appName: yield this.getAppName(),
                appVersion: yield this.getAppVersion(),
            };
        });
    }
    getAppType() {
        return __awaiter(this, void 0, void 0, function* () {
            const { pkgName } = yield prompt({
                type: "list",
                name: "pkgName",
                message: "Please select the application type",
                choices: constant_1.APP_LIST,
            });
            return pkgName;
        });
    }
    getAppName() {
        return __awaiter(this, void 0, void 0, function* () {
            const { appName } = yield prompt({
                type: "input",
                name: "appName",
                default: "render-app",
                message: "Please enter the applicaiton name:",
                validate(name) {
                    const done = this.async();
                    setTimeout(() => {
                        const validationResult = validateProjectName(name);
                        if (!validationResult.validForNewPackages) {
                            const errMsg = [];
                            const tips = "\n   Please enter a new name";
                            [
                                ...(validationResult.errors || []),
                                ...(validationResult.warnings || []),
                            ].forEach((error) => {
                                errMsg.push(`\n   * ${error}`);
                            });
                            done(chalk.red("The application name " +
                                chalk.blue.bold(`'${name}'`) +
                                " does not follow the npm package naming convention:" +
                                errMsg.join("") +
                                tips));
                            return;
                        }
                        done(null, true);
                    }, 0);
                },
            });
            return appName;
        });
    }
    getAppVersion() {
        return __awaiter(this, void 0, void 0, function* () {
            const { appVersion } = yield prompt({
                type: "input",
                name: "appVersion",
                default: "1.0.0",
                message: "Please enter the application version:",
                validate(value) {
                    const done = this.async();
                    setTimeout(() => {
                        if (!semver.valid(value)) {
                            done(chalk.red("Please enter a valid version"));
                            return;
                        }
                        done(null, true);
                    }, 0);
                },
            });
            return appVersion;
        });
    }
    exec() {
        return __awaiter(this, void 0, void 0, function* () {
            const isContinue = yield this.checkCanContinue();
            if (!isContinue) {
                process.exit(1);
            }
            (0, render_node_utils_1.clearConsole)();
            this.setSpinner();
            const isPkgExist = yield this.package.isLocalPkgExist();
            this.spinner.stop();
            if (!isPkgExist) {
                render_node_utils_1.log.debug("No corresponding version of the" +
                    this.pkgName +
                    " found locally, " +
                    "starting to download from the network");
                yield this.package.install();
            }
            const soureDirPath = yield this.package.getPkgCachePath();
            const targetPath = process.env.LOCAL_DEV_PATH || path.resolve(this.appName);
            fse.removeSync(targetPath);
            fse.ensureDirSync(soureDirPath);
            fse.ensureDirSync(targetPath);
            // 模版文件复制到目标文件夹
            fse.copySync(path.join(soureDirPath, "template"), targetPath);
            const pkgJsonPath = path.resolve(targetPath, "package.json");
            const pkgJson = fse.readJSONSync(pkgJsonPath);
            pkgJson.name = this.appName;
            pkgJson.version = this.appVersion;
            fse.writeJsonSync(path.resolve(targetPath, "package.json"), pkgJson, {
                spaces: 2,
                EOL: os.EOL,
            });
            render_node_utils_1.log.info(`🎉 Successfully created app ${this.appName}`);
            render_node_utils_1.log.info("👉 Get started with the following commands:");
            console.log();
            console.log(`${chalk.blue(`$ cd ${this.appName}`)}`);
            console.log(`${chalk.blue("$ npm i")}`);
            console.log();
        });
    }
    checkCanContinue() {
        return __awaiter(this, void 0, void 0, function* () {
            const cwdPath = process.env.LOCAL_DEV_PATH || path.resolve(this.appName);
            if (!this.isCwdPathEmpty(cwdPath)) {
                if (!this.force) {
                    const { isContinue } = yield prompt({
                        type: "confirm",
                        name: "isContinue",
                        default: true,
                        message: chalk.yellow("Current directory (" +
                            this.appName +
                            ") is not empyt. " +
                            "Do you want to continue with the operation:"),
                    });
                    if (!isContinue) {
                        return;
                    }
                }
                const { isDelete } = yield prompt({
                    type: "confirm",
                    name: "isDelete",
                    default: true,
                    message: chalk.yellow("Continuing with the operation will delete " +
                        "all the files in the current directory (" +
                        this.appName +
                        ") :"),
                });
                return isDelete;
            }
            return true;
        });
    }
    isCwdPathEmpty(cwdPath) {
        fse.ensureDirSync(cwdPath);
        const fileList = fse.readdirSync(cwdPath);
        return !fileList || fileList.length === 0;
    }
    setSpinner() {
        this.spinner = (0, ora_1.default)("").start();
        this.spinner.color = "yellow";
        this.spinner.text = "starting to download\n";
    }
}
module.exports = (rest, options, cmd) => {
    return new InitCommand(rest, options, cmd);
};
